import { nextTick, onMounted, unref, watch } from "vue";

// const DEFAULT_CONFIG = {
//   interval: 0.1, // 每次缩放的最小间隔
//   minScale: 0.5, // 最小缩放比例
//   maxScale: 3, // 最大的缩放比例
//   slopOver: false, // 允许超出父元素
//   disabledZoom: false, // 禁止缩放
//   disabledDrag: false, // 禁止拖动
//   dragClass: '', // 拖拽的 class，可以在拖拽的时候指定鼠标样式
//   zoomAnimationTime: 0.5 // 缩放时的动画时间，默认 0.5
// }
/**
 * 元素缩放、拖拽
 * @param {*} el 元素选择器或者一个元素
 * @param {object} [config] 其他选项
 * @param {number} [config.zoomInterval = 0.1] 每次叠加的间隔数
 * @param {number} [config.minZoomScale = 0.5] 最小缩放
 * @param {number} [config.maxZoomScale = 3] 最大缩放
 * @param {boolean} [config.zoom = true] 是否禁用缩放，默认 否
 * @param {boolean} [config.drag = true] 是否禁用拖拽，默认 否
 * @param {boolean} [config.slopOver = false] 是否可以超出父容器边界，默认 否
 * @param {number} [config.zoomAnimationTime = 0.5] 缩放的动画时间
 * @param {string} [config.dragClass] 正在拖动 class，参考 vue-draggable-resizable
 */
export function useZoom (el, config = {}) {
  // 用作鼠标移动，记录 Translate 的坐标值
  let prevTranslateMap = { x: 0, y: 0 };
  let mx = 0; // 记录鼠标按下时的 x 坐标
  let my = 0; // 记录鼠标按下时的 y 坐标
  let tLeft = 0; // 最后设置的 translateX 值
  let tTop = 0; // 最后设置的 translateY 值
  let newsetWidth = 0; // 拖动容器最新的宽度
  let newsetHeight = 0; // 拖动容器最新的高度
  let firstMoveFlag = false; // 第一次移动标记，防止用户第一次按下和松开鼠标但并未移动，第二次移动时 dom 出现闪现的情况
  // 初始化的比例
  let scale = 1;
  let domRect = null
  let pDom = null
  let lastWidth = 0;
  let lastHeight = 0;
  const {
    interval = 0.1, // 每次缩放的最小间隔
    minScale = 0.5, // 最小缩放比例
    maxScale = 3, // 最大的缩放比例
    slopOver = false, // 允许超出父元素
    dragClass, // 拖拽的 class，可以在拖拽的时候指定鼠标样式
    zoom = true,
    drag = true,
    zoomAnimationTime = 0.5, // 缩放时的动画时间，默认 0.5
    buttons = [0], // 0 左键 | 1 齿轮 | 2 右键
  } = config;

  let zoomDom = null;

  onMounted(async () => {
    await nextTick()
    onInit()
  })

  function openZoom () {
    if (!zoomDom) {
      const unwatch = watch(el, () => {
        setTimeout(openZoom)
        unwatch()
      })
      return
    }
    zoom && zoomDom.addEventListener('wheel', onMousewheel)
    drag && zoomDom.addEventListener('mousedown', onMousedown)
  }
  function stopZoom () {
    if (!zoomDom) return;
    zoomDom.removeEventListener('wheel', onMousewheel)
    zoomDom.removeEventListener('mousedown', onMousedown)
  }
  function onInit () {
    zoomDom = unref(el).$el
    zoomDom.style.transformOrigin = '0 0';
    pDom = zoomDom.parentElement;
    const { scrollTop, scrollLeft } = getScrollTopAndLeft(pDom);
    const rect = zoomDom.getBoundingClientRect()
    domRect = {
      width: rect.width,
      height: rect.height,
      top: rect.top + scrollTop,
      left: rect.left + scrollLeft,
    };
    lastWidth = domRect.width;
    lastHeight = domRect.height;
  }
  function onMousewheel (ev) {
    zoomDom.style.cssText += `transition: all ${zoomAnimationTime}s;`;
    const isZoomOut = ev.deltaY < 0; // 缩小
    // 鼠标坐标
    const { x: mouseX, y: mouseY } = ev;
    // 防止因为页面滚动的原因发生计算比例（yScale | xScale）出现误差
    const { scrollTop, scrollLeft } = getScrollTopAndLeft(pDom);
    if (isZoomOut) {
      // 缩小
      scale -= interval;
      if (minScale && scale < minScale) {
        scale = minScale;
      }
    } else {
      // 放大
      scale += interval;
      if (maxScale && scale > maxScale) {
        scale = maxScale;
      }
    }
    // 放大后的宽高
    newsetWidth = domRect.width * scale;
    newsetHeight = domRect.height * scale;
    // 获取比例
    let yScale = (mouseY - domRect.top + scrollTop - prevTranslateMap.y) / lastHeight;
    let xScale = (mouseX - domRect.left + scrollLeft - prevTranslateMap.x) / lastWidth;
    // 需要重新运算的 translate 坐标
    const y = yScale * (newsetHeight - lastHeight);
    const x = xScale * (newsetWidth - lastWidth);
    // 更新
    lastWidth = newsetWidth;
    lastHeight = newsetHeight;
    const translateY = prevTranslateMap.y - y;
    const translateX = prevTranslateMap.x - x;
    zoomDom.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
    // 记录这次的值
    prevTranslateMap = { x: translateX, y: translateY };
    ev.preventDefault();
  }
  function onMousedown (ev) {
    if (!buttons.includes(ev.button)) {
      return
    }
    mx = ev.x;
    my = ev.y;
    dragClass && zoomDom.classList.add(dragClass);
    zoomDom.style.cssText += `transition: all 0s;`;
    // 鼠标移动
    document.addEventListener('mousemove', onMousemove);
    // 鼠标松开
    document.addEventListener('mouseup', onMouseup);
  }
  function onMousemove (ev) {
    firstMoveFlag = true;
    tTop = prevTranslateMap.y + (ev.y - my);
    tLeft = prevTranslateMap.x + (ev.x - mx);
    if (!slopOver) {
      if (tTop < 0) tTop = 0;
      if (tLeft < 0) tLeft = 0;
      const rightBoundary = pDom.offsetWidth - newsetWidth; // 右边边界
      const bottomBoundary = pDom.offsetHeight - newsetHeight; // 下边边界
      if (tTop > bottomBoundary) tTop = bottomBoundary;
      if (tLeft > rightBoundary) tLeft = rightBoundary;
    }
    // 设置样式
    zoomDom.style.cssText += `transform: translate(${tLeft}px, ${tTop}px) scale(${scale})`;
  }
  function onMouseup () {
    if (firstMoveFlag) {
      prevTranslateMap = { x: tLeft, y: tTop };
    }
    firstMoveFlag = false;
    dragClass && zoomDom.classList.remove(dragClass);
    document.removeEventListener('mousemove', onMousemove);
    document.removeEventListener('mouseup', onMouseup);
  }

  return { openZoom, stopZoom }
}

/**
 * 获取滚动的参数
 * @return {{scrollLeft: number, scrollTop: number}}
 */
function getScrollTopAndLeft (dom) {
  let parent = dom;
  let top = 0;
  let left = 0;
  do {
    top += parent.scrollTop;
    left += parent.scrollLeft;
    parent = parent.parentElement;
  } while (parent);
  return { scrollTop: top, scrollLeft: left };
}
